package com.tools.cleanmaster.ui.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;

import com.tools.cleanmaster.R;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.Utils;

/**
 * Created by apolunor on 15/11/25.
 */
public class ProgressBarView extends View {
    private static final boolean DEBUG = FeatureConfig.DEBUG_LOG;
    private static final String TAG = ProgressBarView.class.getSimpleName();

    private static final int CIRCLE_MODE_BIG = 1;
    private static final int CIRCLE_MODE_SMALL = 2;

    public static final float MAX_SWEEP_ANGLE = 300f;

    private boolean mIsBigMode = false;

    private ProgressBar mProgressBar;

    private int mCircleRadius;

    public ProgressBarView(Context context) {
        this(context, null);
    }

    public ProgressBarView(Context context, AttributeSet attrs) {
        super(context, attrs);

        parseAttributes(context.obtainStyledAttributes(attrs, R.styleable.ProgressBarView));
        initView(context);
    }

    private void parseAttributes(TypedArray a) {
        int circleMode = a.getInt(R.styleable.ProgressBarView_circleMode, CIRCLE_MODE_SMALL);

        if (circleMode == CIRCLE_MODE_BIG) {
            mIsBigMode = true;
        }

        a.recycle();
    }

    private void initView(Context context) {
        float barWidth = Utils.dip2px(context,
                mIsBigMode ? ProgressBar.DEFAULT_BAR_WIDTH_BIG : ProgressBar.DEFAULT_BAR_WIDTH);
        // 大样式 loading 不画 rim
        mProgressBar = new ProgressBar(barWidth, MAX_SWEEP_ANGLE, !mIsBigMode);

        mCircleRadius = Utils.dip2px(context,
                mIsBigMode ? ProgressBar.DEFAULT_CIRCLE_RADIUS_BIG : ProgressBar.DEFAULT_CIRCLE_RADIUS_SMALL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int viewWidth = mCircleRadius + this.getPaddingLeft() + this.getPaddingRight();
        int viewHeight = mCircleRadius + this.getPaddingTop() + this.getPaddingBottom();

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width;
        int height;

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(viewWidth, widthSize);
        } else {
            width = viewWidth;
        }

        if (heightMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            height = Math.min(viewHeight, heightSize);
        } else {
            height = viewHeight;
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mProgressBar.setupPaints();
        setupBounds(w, h);

        invalidate();
    }

    private void setupBounds(int width, int height) {
        float barWidth = mProgressBar.mBarWidth;

        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();

        int minValue = Math.min(width - paddingLeft - paddingRight,
                height - paddingBottom - paddingTop);

        float outerCircleDiameter = Math.min(minValue, mCircleRadius * 2 - barWidth * 2);

        float xOffset = (width - paddingLeft - paddingRight - outerCircleDiameter) / 2 + paddingLeft;
        float yOffset = (height - paddingTop - paddingBottom - outerCircleDiameter) / 2 + paddingTop;

        mProgressBar.mCircleBounds = new RectF(xOffset + barWidth,
                yOffset + barWidth,
                xOffset + outerCircleDiameter - barWidth,
                yOffset + outerCircleDiameter - barWidth);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mProgressBar.drawProgressBar(canvas);

        invalidate();
    }

    private static class ProgressBar {
        // defaults in DP
        public static final float DEFAULT_BAR_WIDTH = 1.33f;
        public static final float DEFAULT_BAR_WIDTH_BIG = 3.33f;
        public static final float DEFAULT_CIRCLE_RADIUS_BIG = 46f;
        public static final float DEFAULT_CIRCLE_RADIUS_SMALL = 19.66f;
        // defaults in Millis
        public static final int ONE_SECOND = 1000;
        public static final int PAUSE_GROWING_TIME = 200;
        public static final int BAR_SPIN_CYCLE_TIME = 500;

        public static final float DEFAULT_SPIN_SPEED = 300.0f;

        public static final float ANGLE_360 = 360f;

        //Colors
        private int mBarColor = 0xFFB2B2B2;
        private int mRimColor = 0x0DFFFFFF;

        private float mBarWidth;
        private Paint mBarPaint;
        private Paint mRimPaint;

        private boolean mIsDrawRim;

        public RectF mCircleBounds = new RectF();

        public float mSpinSpeed = DEFAULT_SPIN_SPEED / ONE_SECOND;
        public float mProgress = 0f;

        public float mBarStartSweepAngle = 16f;
        public float mBarExtraAngle = 0f;
        public float mBarDestSweepAngle;

        public boolean mBarGrowingFromFront = true;

        public int mTimeStartGrowing = 0;
        public long mLastTimeAnimated = 0;

        public long mPausedTimeWithoutGrowing = 0;

        private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();

        public ProgressBar (float barWidth, float maxSweep) {
            this(barWidth, maxSweep, false);
        }

        public ProgressBar (float barWidth, float maxSweep, boolean isDrawRim) {
            mBarWidth = barWidth;
            mBarDestSweepAngle = maxSweep - mBarStartSweepAngle;
            mIsDrawRim = isDrawRim;
        }

        public void setupPaints() {
            mBarPaint = new Paint();
            mBarPaint.setColor(mBarColor);
            mBarPaint.setAntiAlias(true);
            mBarPaint.setStyle(Paint.Style.STROKE);
            mBarPaint.setStrokeWidth(mBarWidth);

            mRimPaint = new Paint();
            mRimPaint.setColor(mRimColor);
            mRimPaint.setAntiAlias(true);
            mRimPaint.setStyle(Paint.Style.STROKE);
            mRimPaint.setStrokeWidth(mBarWidth);
        }

        public void drawProgressBar(Canvas canvas) {
            long deltaTime = (SystemClock.uptimeMillis() - mLastTimeAnimated);
            mLastTimeAnimated = SystemClock.uptimeMillis();

            float deltaProgress = deltaTime * mSpinSpeed;
            mProgress += deltaProgress;
            if (mProgress > ANGLE_360) {
                mProgress -= ANGLE_360;
            }
            float startAngle = mProgress;

            updateSweepAngle(deltaTime);
            float sweepAngle = mBarStartSweepAngle + mBarExtraAngle;

            if (mBarPaint == null || mRimPaint == null) {
                setupPaints();
            }

            canvas.drawArc(mCircleBounds, startAngle, sweepAngle, false, mBarPaint);

            if (mIsDrawRim) {
                canvas.drawArc(mCircleBounds, ANGLE_360, ANGLE_360, false, mRimPaint);
            }
        }

        public void updateSweepAngle(long deltaTimeInMilliSeconds) {
            if (mPausedTimeWithoutGrowing >= PAUSE_GROWING_TIME) {
                mTimeStartGrowing += deltaTimeInMilliSeconds;

                if (mTimeStartGrowing > BAR_SPIN_CYCLE_TIME) {
                    mTimeStartGrowing -= BAR_SPIN_CYCLE_TIME;
                    mPausedTimeWithoutGrowing = 0;
                    mBarGrowingFromFront = !mBarGrowingFromFront;
                }

                float distance = mInterpolator.getInterpolation((float) mTimeStartGrowing / BAR_SPIN_CYCLE_TIME);

                if (mBarGrowingFromFront) {
                    mBarExtraAngle = distance * mBarDestSweepAngle;
                } else {
                    float newLength = mBarDestSweepAngle * (1 - distance);
                    mProgress += (mBarExtraAngle - newLength);
                    mBarExtraAngle = newLength;
                }
            } else {
                mPausedTimeWithoutGrowing += deltaTimeInMilliSeconds;
            }
        }
    }

}
